16

简介

webpack可以做的事
  • 代码转换
  • 文件优化
  • 代码分割
  • 模块合并
  • 自动刷新
  • 代码校验
  • 自动发布
面试常见考点
  • webpack常见配置
  • webpack高级配置
  • ast抽象语法树
  • webpack中的Tapable
  • 掌握webpack流程,手写webpack
  • 手写webpack中常见的loader
  • 手写webpack中常见的plugin

webpack基础配置

起步
  • 创建src--->index.js
  • npx webpack
  • 基础配置
//webpack是node写出来的,所以需要node的写法

let path = require('path')   //核心模块

module.exports = {
    mode: 'development',                    //默认两种:production development
    entry: './src/index.js',                //入口
    output: {                               //出口
        filename: 'bundle.js', //打包后的文件名
        path: path.resolve(__dirname, 'dist'), //resolve把相对路径解析成绝对路径,__dirname意思是在当前目录建立一个,路径必须是一个绝对路径
    }
}

script脚本

  • 手动配置:npx webpack --config webpack.config.js
  • 脚本配置:
"scripts": {
    "build": "webpack"
  }

//npm run build 
  • 传参npx webpack -- --config webpack.config.js

Html插件

npx webpack-dev-server

  • 开发服务,内部通过express实现这种服务
  • 并不真实打包文件,只是在内存中生成
htmlWebpackPlugin
  • 将打包后的js文件插入html文件,并放到build目录下
let htmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    devServer: {                                //开发服务器的配置
        port: 3000,                             //设置端口号
        progress: true,                         //进度条
        contentBase: '/.build',                  //指定静态目录
        compress: true
    },
    output: {
        filename: 'bundle.[hash:8].js',         //文件名
        path: path.resolve(__dirname, 'dist'),
    },
    plugins: [                                  //数组 放着所有的webpack插件
        new htmlWebpackPlugin({
            template: './src/index.html',       //模板
            filename: 'index.html',             //打包后的文件名
            minify: {                           //打包后的html也压缩
                removeAttributeQuotes: true,    //删除属性的双引号
                collapseWhitespace: true,       //折叠空行
            },
            hash: true                          //html文件加上哈希戳
        })
    ]
}

css配置

loader:Webpack本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。

Loader 可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果

css配置

  • css-loader 解析@import这种语法
  • style-loadercss插入到head的标签中
  • loader的特点:希望单一
  • loader的用法:字符串只用一个loader,多个loader需要[]
  • loader的顺序:默认是从右向左执行       从下往上

优先级

{
    loader: 'style-loader',
    options: {
        insertAt: 'top'    //确保优先级
    }
}

处理lesssassstylus

  • yarn add less less-loader
  • yarn add node-sass sass-loader
  • yarn add stylus stylus-loader
module.exports = {
    module: {                                       //模块
        rules: [                                    //规则 
                                        //loader的特点:希望单一
            //{ test: /\.css$/, use: ['style-loader', 'css-loader'] }   //第一种:写法
                                        //loader还可以写成对象方式
            {
                                        //处理less文件
                test: /\.css$/,
                use: [{
                        loader: 'style-loader',
                        options: {
                            insertAt: 'top'    //确保优先级
                        }
                    },
                    'css-loader',              //@import  解析路径
                    'less-loader'              //把less ---->css  
                ]
            }
        ]
    }
}

抽离CSS的插件

  • 默认打包后只能插入<style>标签内,我们希望抽离成<link>形式
  • 通过 mini-css-extract-plugin这个插件
  • yarn add mini-css-extract-plugin -D
  • 插件都是类插件的使用顺序没有先后
let MiniCssExtractPlugin = require('mini-css-extract-plugin')

//配置plugin
plugins: [
        new MiniCssExtractPlugin({
            filename: 'main.css',
        })
    ],

//配置module    
module: {
        rules: [{
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
            }
        ]
    }    

自动添加前缀

  • autoprefixer
  • 前提要用postcss-loader
  • yarn add postcss-loader autoprefixer
//配置module
//先处理post-css再处理css
module: {
        rules: [{
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader','postcss-loader']       //加上post-css
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
            }
        ]
    }
//创建postcss.config.js文件并配置

module.exports = {
    plugins: [require('autoprefixer')]
}
压缩css(同时保证js的压缩)
  • 通过optimize-css-assets-webpack-plugin
  • yarn add optmize - css - assets - webpack - plugin - D
  • uglifyjs-webpack-plugin
let path = require('path')
let htmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin')
let OptimizeCss = require('optimize-css-assets-webpack-plugin')
let UglifyJsPlugin = require('uglifyjs-webpakc-plugin')

module.exports = {
    Optimization: { //***优化项,用了这个插件之后就必须用一下Uglifyjs压缩js
        minimizer: [
            new UglifyJsPlugin({
                cache: true, //是否用缓存
                parallel: true, //是否并行打包
                sourceMap: true
            }),
            new OptimizeCss()
        ]
    },
    devServer: {
        port: 3000,
        progress: true,
        contentBase: '/.build'
    },
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.[hash:8].js',
        path: path.resolve(__dirname, 'dist'),
    },
    plugins: [
        new htmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            minify: {
                removeAttributeQuotes: true,
                collapseWhitespace: true,
            },
            hash: true
        }),
        new MiniCssExtractPlugin({
            filename: 'main.css'
        })
    ],
    module: {
        rules: [
            //{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader, //***创建link标签,引用
                    'css-loader',
                    'less-loader',
                    'postcss-loader'

                ]
            }
        ]
    }
}

JS配置

转化es6语法

babel
  • yarn add babel-loader @babel/core @babel/preset-env -D
  • 配置module
module: {
        rules: [{
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {          //用babel-loader es6---->es5
                        presets: [
                            '@babel/preset-env'
                        ]
                    }
                }
            }
        ]
    }
            

配置提案里支持的语法

class A{
    a = 1;
}
  • yarn add @babel/plugin-proposal-class-properties -D
{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                '@babel/preset-env'
            ],
            plugins: [
                '@babel/plugin-proposal-class-properties'
            ]
        }
    }
}

支持装饰器语法

function log(target) {
    console.log(target, '23')
}

log(A)
  • yarn add @babel/plugin-proposal-decorators -D
配置module
module: {
        rules: [{
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: { //用babel-loader es6---->es5
                        presets: [
                            '@babel/preset-env'
                        ],
                        plugins: [
                            ['@babel/plugin-proposal-class-properties'],
                            ['@babel/plugin-proposal-decorators',{"legacy":true}],
                            ['@babel/plugin-transform-runtime']
                        ]
                    }
                }
            }
        ]
    }

处理JS语法及校验

    function *gen(params){
        yield 1;     
    }
    console.log(gen().next());  //内置API
    
// Uncaught ReferenceError: regeneratorRuntime is not defined
babel-runtime
  • yarn add @babel/plugin-transform-runtime -D
  • yarn add @babel/runtime
module: {
        rules: [{
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: { //用babel-loader es6---->es5
                        presets: [
                            '@babel/preset-env'
                        ],
                        plugins: [
                            ['@babel/plugin-proposal-class-properties'], ['@babel/plugin-proposal-decorators',{"legacy":true}],
                            ['@babel/plugin-transform-runtime']
                        ]
                    }
                },
                include:path.resolve(__dirname,'src'),  //包括查找范围
                exclude:/node_module/                   //排除查找范围
            }
        ]
    }
@babel/polyfill
'aaa'.include('a')  //ES7语法

//实例上的方法默认都不会解析    
  • yarn add @babel/polyfill
//a.js
require('@babel/polyfill')
'aaa'.include('a')
eslint
  • yarn add eslint eslint-loader
module: {
        rules: [     //loader默认从右向左执行 从下到上,写的太多容易乱,写到一起删除不方便
            {
                test:\.js$/,
                use:{
                    loader:'eslint-loader',
                    options:{
                        enforce:'pre'   //previous -> normal-> post 顺序执行
                    }
                }
            }
            /*...*/
        ]
}     
  • 选择好配置,下载.eslintrc.jsonconfiguration

全局变量引入问题

  • 第三方模块引用
  • yarn add jquery
import $ form 'jqurey'
console.log($) 
console.log(window.$) //undefined,并不会挂载到window上

如何将变量暴露给window?

expose-loader (内联loader)
  • yarn add expose-loader
import $ from 'expose-loader?$!jquery'   //将jquery作为$暴露给全局
  • 也可以在webpack.config.js中配
module: {
        rules: [{
                test: require.resolve('jquery'),
                use: 'expose-loader?$!jquery'
                }
        ]
    }        
  • 在每个模块中注入$对象
//webpack.config.js
let Webpack = require('webpack')
 new Webpack.ProvidePlugin({
            //再每个模块中都注入$符
    jqurey: '$'
})
//index.js
console.log($)   //只是在每个模块中都注入一个`$`
//此时window.$   //undefined
cdn外部路径引入
<script src="https://cdn.bootcss.com/jquery/3.3.1/core.js"></script>
//如果此时js也引入jquery会导致重复
import $ form jquery
externals: {
        jquery: 'jQuery'   //告诉webpack从外部引入,并不需要打包,忽略
    },
总结
  • expose-loader:暴露到window上
  • providePlugin:给每个提供一个
  • 引入不打包
loader类型
  • pre             前面执行的loader
  • normal        普通的loader
  • liader        内联loader
  • post            后置loader

图片处理

图片引入方式
  • js中创建图片来引入
let image = new Image()
image.src = './logo.png'
document.body.appendChild(image) //打包完,其实就是一个普通的字符串,并没有真正的引入图片

file-loader

  • yarn add file-loader
  • 默认在内部生成图片,到build目录下,并且把生成的路径的名字返回回来
//webpack.config.js

module: {
        rules: [{
                test: /\.(png|jpg|gif)$/,
                use: 'file-loader'
            }
        ]
}
//index.js

import logo from './logo.png' //把图片引入,生成一个哈希戳的logo,返回的结果是一个新的图片

console.log(logo)

let image = new Image();
image.src = logo;
document.body.appendChild(image)
  • css中引入background('url')
  • <img src="" alt="">

打包文件分类

打包多页应用

配置source-map


kinshan
1.7k 声望1.9k 粉丝